/*
 * Copyright (c) 2023-2025 elsfs Authors. All Rights Reserved.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package org.elsfs.cloud.common.mybatis.ext.sql;

import com.baomidou.mybatisplus.core.exceptions.MybatisPlusException;
import com.baomidou.mybatisplus.core.metadata.TableInfoHelper;
import java.util.List;
import java.util.function.Consumer;
import java.util.stream.Collectors;
import org.apache.commons.compress.utils.Lists;
import org.elsfs.cloud.common.core.entity.BaseEntity;
import org.elsfs.cloud.common.mybatis.ext.sql.support.builder.StandardSelectSql;
import org.elsfs.cloud.common.mybatis.ext.sql.support.common.HierarchyTableAliasManager;
import org.elsfs.cloud.common.mybatis.ext.sql.support.common.SimpleTableAliasManager;
import org.elsfs.cloud.common.mybatis.ext.sql.support.condition.ConditionItem;
import org.elsfs.cloud.common.util.lang.StringUtils;

/**
 * 抽象Join实现
 *
 * @param <C> 子类具体类型
 */
@SuppressWarnings("unchecked")
public abstract class AbstractJoin<C extends AbstractJoin<C>> extends CompositeSqlFragment
    implements Join<
            C,
            AbstractJoin<C>.JoinBuilderImpl,
            AbstractJoin<C>.JoinBuilderImpl.JoinConditionBuilderImpl>,
        SqlParameterManagerAware,
        TableAliasManagerAware {

  /** 具体子类实现引用 */
  protected final C childThis = (C) this;

  /** Join构建器列表 */
  private final List<JoinBuilderImpl> joinBuilders = Lists.newArrayList();

  /**
   * 内连接一张表
   *
   * @param entityClass 实体类
   * @return 连接配置对象
   */
  @Override
  public JoinBuilderImpl join(Class<? extends BaseEntity> entityClass) {
    JoinBuilderImpl joinBuilder =
        new JoinBuilderImpl(
            JoinType.INNER_JOIN, TableInfoHelper.getTableInfo(entityClass).getTableName());
    this.joinBuilders.add(joinBuilder);
    return joinBuilder;
  }

  /**
   * 内连接一张表
   *
   * @param entityClass 实体类
   * @param tableAlias 表别名
   * @return 连接配置对象
   */
  @Override
  public JoinBuilderImpl join(Class<? extends BaseEntity> entityClass, String tableAlias) {
    JoinBuilderImpl joinBuilder =
        new JoinBuilderImpl(
            JoinType.INNER_JOIN,
            TableInfoHelper.getTableInfo(entityClass).getTableName(),
            tableAlias);
    this.joinBuilders.add(joinBuilder);
    return joinBuilder;
  }

  /**
   * 内连接一张表
   *
   * @param entityClass 实体类
   * @param tableAlias 表别名
   * @param registerAlias 是否注册别名
   * @return 连接配置对象
   */
  @Override
  public JoinBuilderImpl join(
      Class<? extends BaseEntity> entityClass, String tableAlias, boolean registerAlias) {
    JoinBuilderImpl joinBuilder =
        new JoinBuilderImpl(
            JoinType.INNER_JOIN,
            TableInfoHelper.getTableInfo(entityClass).getTableName(),
            tableAlias,
            registerAlias);
    this.joinBuilders.add(joinBuilder);
    return joinBuilder;
  }

  /**
   * 内连接一张表
   *
   * @param table 数据库表名
   * @return 连接配置对象
   */
  @Override
  public JoinBuilderImpl join(String table) {
    JoinBuilderImpl joinBuilder = new JoinBuilderImpl(JoinType.INNER_JOIN, table);
    this.joinBuilders.add(joinBuilder);
    return joinBuilder;
  }

  /**
   * 内连接一张表
   *
   * @param table 数据库表名
   * @param tableAlias 表别名
   * @return 连接配置对象
   */
  @Override
  public JoinBuilderImpl join(String table, String tableAlias) {
    JoinBuilderImpl joinBuilder = new JoinBuilderImpl(JoinType.INNER_JOIN, table, tableAlias);
    this.joinBuilders.add(joinBuilder);
    return joinBuilder;
  }

  /**
   * 内连接一张表
   *
   * @param table 数据库表名
   * @param tableAlias 表别名
   * @param registerAlias 是否注册别名
   * @return 连接配置对象
   */
  @Override
  public JoinBuilderImpl join(String table, String tableAlias, boolean registerAlias) {
    JoinBuilderImpl joinBuilder =
        new JoinBuilderImpl(JoinType.INNER_JOIN, table, tableAlias, registerAlias);
    this.joinBuilders.add(joinBuilder);
    return joinBuilder;
  }

  /**
   * 内连接一个子查询
   *
   * @param sqlBuilderConsumer 子查询构建器
   * @param tableAlias 表别名
   * @return 连接配置对象
   */
  @Override
  public JoinBuilderImpl join(Consumer<StandardSelectSql> sqlBuilderConsumer, String tableAlias) {
    return this.doSubQueryJoin(sqlBuilderConsumer, tableAlias, JoinType.INNER_JOIN);
  }

  /**
   * 左连接一张表
   *
   * @param entityClass 实体类
   * @return 连接配置对象
   */
  @Override
  public JoinBuilderImpl leftJoin(Class<? extends BaseEntity> entityClass) {
    JoinBuilderImpl joinBuilder =
        new JoinBuilderImpl(
            JoinType.LEFT_JOIN, TableInfoHelper.getTableInfo(entityClass).getTableName());
    this.joinBuilders.add(joinBuilder);
    return joinBuilder;
  }

  /**
   * 左连接一张表
   *
   * @param entityClass 实体类
   * @param tableAlias 表别名
   * @return 连接配置对象
   */
  @Override
  public JoinBuilderImpl leftJoin(Class<? extends BaseEntity> entityClass, String tableAlias) {
    JoinBuilderImpl joinBuilder =
        new JoinBuilderImpl(
            JoinType.LEFT_JOIN,
            TableInfoHelper.getTableInfo(entityClass).getTableName(),
            tableAlias);
    this.joinBuilders.add(joinBuilder);
    return joinBuilder;
  }

  /**
   * 左连接一张表
   *
   * @param entityClass 实体类
   * @param tableAlias 表别名
   * @param registerAlias 是否注册别名
   * @return 连接配置对象
   */
  @Override
  public JoinBuilderImpl leftJoin(
      Class<? extends BaseEntity> entityClass, String tableAlias, boolean registerAlias) {
    JoinBuilderImpl joinBuilder =
        new JoinBuilderImpl(
            JoinType.LEFT_JOIN,
            TableInfoHelper.getTableInfo(entityClass).getTableName(),
            tableAlias,
            registerAlias);
    this.joinBuilders.add(joinBuilder);
    return joinBuilder;
  }

  /**
   * 左连接一张表
   *
   * @param table 数据库表名
   * @return 连接配置对象
   */
  @Override
  public JoinBuilderImpl leftJoin(String table) {
    JoinBuilderImpl joinBuilder = new JoinBuilderImpl(JoinType.LEFT_JOIN, table);
    this.joinBuilders.add(joinBuilder);
    return joinBuilder;
  }

  /**
   * 左连接一张表
   *
   * @param table 数据库表名
   * @param tableAlias 表别名
   * @return 连接配置对象
   */
  @Override
  public JoinBuilderImpl leftJoin(String table, String tableAlias) {
    JoinBuilderImpl joinBuilder = new JoinBuilderImpl(JoinType.LEFT_JOIN, table, tableAlias);
    this.joinBuilders.add(joinBuilder);
    return joinBuilder;
  }

  /**
   * 左连接一张表
   *
   * @param table 数据库表名
   * @param tableAlias 表别名
   * @param registerAlias 是否注册别名
   * @return 连接配置对象
   */
  @Override
  public JoinBuilderImpl leftJoin(String table, String tableAlias, boolean registerAlias) {
    JoinBuilderImpl joinBuilder =
        new JoinBuilderImpl(JoinType.LEFT_JOIN, table, tableAlias, registerAlias);
    this.joinBuilders.add(joinBuilder);
    return joinBuilder;
  }

  /**
   * 左连接一个子查询
   *
   * @param sqlBuilderConsumer 子查询构建器
   * @param tableAlias 表别名
   * @return 连接配置对象
   */
  @Override
  public JoinBuilderImpl leftJoin(
      Consumer<StandardSelectSql> sqlBuilderConsumer, String tableAlias) {
    return this.doSubQueryJoin(sqlBuilderConsumer, tableAlias, JoinType.LEFT_JOIN);
  }

  /**
   * 右连接一张表
   *
   * @param entityClass 实体类
   * @return 连接配置对象
   */
  @Override
  public JoinBuilderImpl rightJoin(Class<? extends BaseEntity> entityClass) {
    JoinBuilderImpl joinBuilder =
        new JoinBuilderImpl(
            JoinType.RIGHT_JOIN, TableInfoHelper.getTableInfo(entityClass).getTableName());
    this.joinBuilders.add(joinBuilder);
    return joinBuilder;
  }

  /**
   * 右连接一张表
   *
   * @param entityClass 实体类
   * @param tableAlias 表别名
   * @return 连接配置对象
   */
  @Override
  public JoinBuilderImpl rightJoin(Class<? extends BaseEntity> entityClass, String tableAlias) {
    JoinBuilderImpl joinBuilder =
        new JoinBuilderImpl(
            JoinType.RIGHT_JOIN,
            TableInfoHelper.getTableInfo(entityClass).getTableName(),
            tableAlias);
    this.joinBuilders.add(joinBuilder);
    return joinBuilder;
  }

  /**
   * 右连接一张表
   *
   * @param entityClass 实体类
   * @param tableAlias 表别名
   * @param registerAlias 是否注册别名
   * @return 连接配置对象
   */
  @Override
  public JoinBuilderImpl rightJoin(
      Class<? extends BaseEntity> entityClass, String tableAlias, boolean registerAlias) {
    JoinBuilderImpl joinBuilder =
        new JoinBuilderImpl(
            JoinType.RIGHT_JOIN,
            TableInfoHelper.getTableInfo(entityClass).getTableName(),
            tableAlias,
            registerAlias);
    this.joinBuilders.add(joinBuilder);
    return joinBuilder;
  }

  /**
   * 右连接一张表
   *
   * @param table 数据库表名
   * @return 连接配置对象
   */
  @Override
  public JoinBuilderImpl rightJoin(String table) {
    JoinBuilderImpl joinBuilder = new JoinBuilderImpl(JoinType.RIGHT_JOIN, table);
    this.joinBuilders.add(joinBuilder);
    return joinBuilder;
  }

  /**
   * 右连接一张表
   *
   * @param table 数据库表名
   * @param tableAlias 表别名
   * @return 连接配置对象
   */
  @Override
  public JoinBuilderImpl rightJoin(String table, String tableAlias) {
    JoinBuilderImpl joinBuilder = new JoinBuilderImpl(JoinType.RIGHT_JOIN, table, tableAlias);
    this.joinBuilders.add(joinBuilder);
    return joinBuilder;
  }

  /**
   * 右连接一张表
   *
   * @param table 数据库表名
   * @param tableAlias 表别名
   * @param registerAlias 是否注册别名
   * @return 连接配置对象
   */
  @Override
  public JoinBuilderImpl rightJoin(String table, String tableAlias, boolean registerAlias) {
    JoinBuilderImpl joinBuilder =
        new JoinBuilderImpl(JoinType.RIGHT_JOIN, table, tableAlias, registerAlias);
    this.joinBuilders.add(joinBuilder);
    return joinBuilder;
  }

  /**
   * 右连接一个子查询
   *
   * @param sqlBuilderConsumer 子查询构建器
   * @param tableAlias 表别名
   * @return 连接配置对象
   */
  @Override
  public JoinBuilderImpl rightJoin(
      Consumer<StandardSelectSql> sqlBuilderConsumer, String tableAlias) {
    return this.doSubQueryJoin(sqlBuilderConsumer, tableAlias, JoinType.RIGHT_JOIN);
  }

  /**
   * 子查询JOIN
   *
   * @param sqlBuilderConsumer 子查询SQL构建器
   * @param tableAlias 临时表别名
   * @param joinType 连接类型
   * @return 连接配置对象
   */
  private JoinBuilderImpl doSubQueryJoin(
      Consumer<StandardSelectSql> sqlBuilderConsumer, String tableAlias, JoinType joinType) {
    if (StringUtils.isBlank(tableAlias)) {
      throw new MybatisPlusException("JOIN子查询的表别名不能为空");
    }

    StandardSelectSql standardSelectSql =
        new StandardSelectSql(
            this.getSqlParameterManager(),
            new HierarchyTableAliasManager(
                new SimpleTableAliasManager(), this.getTableAliasManager()));
    sqlBuilderConsumer.accept(standardSelectSql);

    JoinBuilderImpl joinBuilder =
        new JoinBuilderImpl(joinType, "(" + standardSelectSql.buildSqlFragment() + ")", tableAlias);
    this.joinBuilders.add(joinBuilder);
    return joinBuilder;
  }

  /** 构建SQL之前处理 */
  @Override
  protected void beforeBuild() {
    for (JoinBuilderImpl joinBuilder : this.joinBuilders) {
      this.addSqlFragment(joinBuilder);
    }
  }

  /** Join构建器实现 */
  public class JoinBuilderImpl extends CompositeSqlFragment
      implements JoinBuilder<JoinBuilderImpl, JoinBuilderImpl.JoinConditionBuilderImpl, C>,
          SqlParameterManagerAware,
          TableAliasManagerAware {

    /** 连接类型 */
    private final JoinType joinType;

    /** 连接表名称 */
    private final String table;

    /** 连接表别名 */
    private final String tableAlias;

    /** Join条件构建器 */
    private JoinConditionBuilderImpl joinConditionBuilder;

    /**
     * 构造方法
     *
     * @param joinType 连接类型
     * @param table 数据库表名
     * @param tableAlias 表别名
     * @param registerAlias 是否注册别名
     */
    public JoinBuilderImpl(
        JoinType joinType, String table, String tableAlias, boolean registerAlias) {
      this.joinType = joinType;
      this.table = table;
      this.tableAlias = tableAlias;

      if (registerAlias) {
        this.getTableAliasManager().registerAlias(table, tableAlias);
      }
    }

    public JoinBuilderImpl(JoinType joinType, String table, String tableAlias) {
      this(joinType, table, tableAlias, true);
    }

    public JoinBuilderImpl(JoinType joinType, String table) {
      this(joinType, table, AbstractJoin.this.getTableAliasManager().generateAlias(table), true);
    }

    /**
     * 返回上一级对象
     *
     * @return 上级对象
     */
    @Override
    public C end() {
      return AbstractJoin.this.childThis;
    }

    /**
     * 连接条件设置
     *
     * @return 连接条件构建器
     */
    @Override
    public JoinConditionBuilderImpl on() {
      this.joinConditionBuilder = new JoinConditionBuilderImpl();
      return this.joinConditionBuilder;
    }

    /** 构建SQL之前处理 */
    @Override
    protected void beforeBuild() {
      this.addSqlFragment(new TokenSqlFragment(this.joinType.getJoinKeyword()));
      this.addSqlFragment(new TokenSqlFragment(this.table + " AS " + this.tableAlias));
      this.addSqlFragment(this.joinConditionBuilder);
    }

    /**
     * 获取SQL参数管理器
     *
     * @return SQL参数管理器
     */
    @Override
    public SqlParameterManager getSqlParameterManager() {
      return AbstractJoin.this.getSqlParameterManager();
    }

    /**
     * 获取表别名管理器
     *
     * @return 表名别管理器
     */
    @Override
    public TableAliasManager getTableAliasManager() {
      return AbstractJoin.this.getTableAliasManager();
    }

    /** Join条件构建器实现 */
    public class JoinConditionBuilderImpl extends AbstractCondition<JoinConditionBuilderImpl>
        implements JoinConditionBuilder<JoinConditionBuilderImpl, JoinBuilderImpl, C> {

      /** Join条件列表 */
      private final List<ConditionItem> conditionItems = Lists.newArrayList();

      /**
       * 获取SQL参数管理器
       *
       * @return SQL参数管理器
       */
      @Override
      public SqlParameterManager getSqlParameterManager() {
        return JoinBuilderImpl.this.getSqlParameterManager();
      }

      /**
       * 添加一个条件
       *
       * @param conditionItem 条件
       */
      @Override
      protected void addConditionItem(ConditionItem conditionItem) {
        this.conditionItems.add(conditionItem);
      }

      /**
       * 返回上一级对象
       *
       * @return 上级对象
       */
      @Override
      public C end() {
        return JoinBuilderImpl.this.end();
      }

      /**
       * 构建SQL片段
       *
       * @return SQL片段
       */
      @Override
      public String buildSqlFragment() {
        if (this.conditionItems.isEmpty()) {
          return "";
        }

        return String.format(
            "ON %s",
            ConditionItem.stripHeaderKeyword(
                this.conditionItems.stream()
                    .map(ConditionItem::buildSqlFragment)
                    .collect(Collectors.joining(" "))));
      }

      /**
       * 获取表别名管理器
       *
       * @return 表名别管理器
       */
      @Override
      public TableAliasManager getTableAliasManager() {
        return JoinBuilderImpl.this.getTableAliasManager();
      }
    }
  }
}
